package org.hcf.api.controller;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.hcf.api.ErrorCode;
import org.hcf.api.base.CaptchaGenerator;
import org.hcf.api.base.Helper;
import org.hcf.api.base.Const;
import org.hcf.api.base.SnowflakeUtils;
import org.hcf.api.http.LoginRequest;
import org.hcf.api.http.RegisterRequest;
import org.hcf.common.util.ImageUtil;
import org.hcf.common.util.MD5Util;
import org.hcf.data.RedisUtils;
import org.hcf.data.SqlHelper;
import org.hcf.data.cache.CacheConfig;
import org.hcf.data.cache.Caches;
import org.hcf.data.entity.ChatUser;
import org.hcf.data.service.IChatUserService;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.io.*;

@Slf4j
@RestController
@Order(99)
@RequestMapping("/auth")
/** 用户验证 */
public class AuthController {
    String secret = "f2708c8fe04ed10a032f1aafcb17bd256c7e9940da4115cf8ba84bd21e9e7c64";


    @Data
    public static class HttpResponse {
        int code;
        String msg;
        JSONObject data;

        public HttpResponse(ErrorCode code) {
            this(code.NUM, code.DIC);
        }

        public HttpResponse(int code, String msg) {
            this(code, msg, null);
        }

        public HttpResponse(JSONObject data) {
            this(ErrorCode.SUCCESS.NUM, ErrorCode.SUCCESS.DIC, data);
        }

        public HttpResponse(int code, String msg, JSONObject data) {
            this.code = code;
            this.msg = msg;
            this.data = data;
        }
    }

//    @Autowired
//    TencentConf tencentConf;
//    @Autowired
//    ResConf res;
//    static TencentConf conf;
//    static TLSSigAPIv2 api;

//    @PostConstruct
//    void init() {
//        conf = tencentConf;
//        api = new TLSSigAPIv2(conf.getAppId(), conf.getAppSecret());
//    }

    //    @RequestHeader("code") String code, @RequestHeader("time") String time,
    @CrossOrigin(origins = "*")
    @PostMapping("/login")
    /** 用户登录 */
    public JSONObject login(@RequestBody JSONObject request) {
        LoginRequest req = request.toJavaObject(LoginRequest.class);
//        if (!StrUtil.equals(MD5Util.encode(secret + "_" + time), code)) return toResponse(ErrorCode.SECRET_ERROR);
        if (StrUtil.isBlank(req.getAccount()) || StrUtil.isBlank(req.getPwd()))
            return toResponse(ErrorCode.PARAM_ERROR);
        ChatUser user = RedisUtils.getAccount(req.getAccount(), false);
        if (user == null) return toResponse(ErrorCode.USER_NOT_EXISTS);
        if (!StrUtil.equalsIgnoreCase(user.getPassword(), req.getPwd())) return toResponse(ErrorCode.PASSWORD_ERROR);

        String sig = Helper.getUserSig(user.getUserId());

        long session = SnowflakeUtils.nextId();
        JSONObject data = new JSONObject();
        data.put("userSig", sig);
        data.put("userId", user.getUserId());
        data.put("session", String.valueOf(session));
        Caches.getCache(CacheConfig.USER_LOGIN_TOKEN).put(user.getUserId().toString(), session);
        log.info("登录成功:" + data);
        return toResponse(data);
    }

    @CrossOrigin(origins = "*")
    @PostMapping("/updateSession")
    public JSONObject updateSession(@RequestBody JSONObject request) {
        LoginRequest req = request.toJavaObject(LoginRequest.class);
//        if (!StrUtil.equals(MD5Util.encode(secret + "_" + time), code)) return toResponse(ErrorCode.SECRET_ERROR);
        if (StrUtil.isBlank(req.getAccount()) || StrUtil.isBlank(req.getPwd()))
            return toResponse(ErrorCode.PARAM_ERROR);
        ChatUser user = RedisUtils.getAccount(req.getAccount(), false);
        if (user == null) return toResponse(ErrorCode.USER_NOT_EXISTS);
        if (!StrUtil.equalsIgnoreCase(user.getPassword(), req.getPwd())) return toResponse(ErrorCode.PASSWORD_ERROR);

//        String sig = Helper.getUserSig(user.getUserId());

        long session = SnowflakeUtils.nextId();
        JSONObject data = new JSONObject();
//        data.put("userSig", sig);
        data.put("userId", user.getUserId());
        data.put("session", String.valueOf(session));
        Caches.getCache(CacheConfig.USER_LOGIN_TOKEN).put(user.getUserId().toString(), session);
        log.info("刷新身份成功:" + data);
        return toResponse(data);

    }
//    @GetMapping("/login_get")
//    /** 腾讯登录 GET */
//    public JSONObject getLogin(@RequestParam("account") String account, @RequestParam("pwd") String pwd, HttpServletRequest request) {
//        if (StrUtil.isBlank(account) || StrUtil.isBlank(pwd))
//            return toResponse(ErrorCode.PARAM_ERROR);
//        ChatUser user = RedisUtils.getAccount(account, false);
//        if (user == null) return toResponse(ErrorCode.USER_NOT_EXISTS);
//        if (!StrUtil.equalsIgnoreCase(user.getPassword(), pwd)) return toResponse(ErrorCode.PASSWORD_ERROR);
//        String sig = Helper.getUserSig(user.getUserId());
//
//        JSONObject data = new JSONObject();
//        data.put("userSig", sig);
//        data.put("userId", user.getUserId());
//
//        return toResponse(data);
//    }

//    @GetMapping("register_get")
//    /** 用户注册 GET */
//    public JSONObject getRegister(@RequestParam("account") String account, @RequestParam("pwd") String pwd, @RequestParam("pic") String pic, @RequestParam("nick") String nick) {
//        if (StrUtil.isBlank(account) || StrUtil.isBlank(pwd) || StrUtil.isBlank(nick))
//            return toResponse(ErrorCode.PARAM_ERROR);
//        ChatUser user = RedisUtils.getAccount(account);
//        if (user != null) return toResponse(ErrorCode.USER_EXISTS);
//        user = new ChatUser();
//        user.setUserId(getNewUserId());
//        user.setAccount(account);
//        user.setNickName(nick);
//        user.setPassword(MD5Util.encode(pwd));
//        if (SqlHelper.getService(IChatUserService.class).save(user)) {
//            saveAvatar(user, pic);
//            SqlHelper.getService(IChatUserService.class).updateById(user);
//            RedisUtils.putUser(user);
////            String url = conf.getUrl() + "/v4/im_open_login_svc/account_import?sdkappid=" + conf.getAppId() + "&identifier=" + conf.getAdminUserName() + "&usersig=" + getAdminSig() + "&random=" + RandomUtil.randomInt(0, Integer.MAX_VALUE) + "&contenttype=json";
//            String url = Helper.getAPI(Const.API.ACCOUNT_IMPORT);
//            JSONObject json = new JSONObject();
//            json.put("UserID", user.getUserId());
//            json.put("Nick", user.getNickName());
//            json.put("FaceUrl", user.getAvatar());
//            log.info(HttpUtil.post(url, json.toJSONString()));
//            return toResponse(ErrorCode.SUCCESS);
//        }
//        return toResponse(ErrorCode.CREATE_USER_ERROR);
//    }

    @CrossOrigin(origins = "*")
    @PostMapping("/register")
    /** 用户注册 */
    public JSONObject register(@RequestBody String body) {
        JSONObject request = JSONObject.parseObject(body);
        RegisterRequest req = request.toJavaObject(RegisterRequest.class);

//        if (!StrUtil.equals(MD5Util.encode(secret + "_" + time), code)) return toResponse(ErrorCode.SECRET_ERROR);
        if (StrUtil.isBlank(req.getAccount()) || StrUtil.isBlank(req.getPwd()) || StrUtil.isBlank(req.getNick()) || StrUtil.isBlank(req.getCode()))
            return toResponse(ErrorCode.PARAM_ERROR);
        String code = Caches.getCache(CacheConfig.REGISTER_CODE).get(req.getAccount(), String.class);
        if (code == null || !code.equals(req.getCode())) return toResponse(ErrorCode.REGISTER_CODE_ERROR);
        Caches.getCache(CacheConfig.REGISTER_CODE).remove(req.getAccount());
        ChatUser user = RedisUtils.getAccount(req.getAccount());
        if (user != null) return toResponse(ErrorCode.USER_EXISTS);
        user = new ChatUser();
        user.setUserId(getNewUserId());
        user.setAccount(req.getAccount());
        user.setNickName(req.getNick());
        user.setPassword(MD5Util.encode(req.getPwd()));
        if (!saveAvatar(user, req.getPic())) return toResponse(ErrorCode.SAVE_AVATAR_ERROR);
        if (SqlHelper.getService(IChatUserService.class).save(user)) {
//            SqlHelper.getService(IChatUserService.class).updateById(user);
            RedisUtils.putUser(user);
            String url = Helper.getAPI(Const.API.ACCOUNT_IMPORT);
            JSONObject json = new JSONObject();
            json.put("UserID", user.getUserId().toString());
            json.put("Nick", user.getNickName());
            json.put("FaceUrl", user.getAvatar());
            log.info("api请求地址:" + url + ", body:" + json);
            log.info(HttpUtil.post(url, json.toJSONString()));
            return toResponse(ErrorCode.SUCCESS);
        }
        return toResponse(ErrorCode.CREATE_USER_ERROR);
    }

    @CrossOrigin(origins = "*")
    @PostMapping("/getRegisterCode")
    public JSONObject getRegisterCode(@RequestBody String body, HttpServletRequest httpServletRequest) {
        JSONObject request = JSONObject.parseObject(body);
        System.out.println("host： " + httpServletRequest.getRemoteHost());
        String account = request.getString("account");
        if (StrUtil.isBlank(account)) return toResponse(ErrorCode.PARAM_ERROR);
        Boolean check = Caches.getCache(CacheConfig.REGISTER_CODE_IP).get(httpServletRequest.getRemoteHost(), Boolean.class);
        if (check != null && !check) return toResponse(ErrorCode.HAS_REGISTER_CODE);
        String code = null;
        byte[] bs = null;
        while ((bs = CaptchaGenerator.getRandomImage((code = CaptchaGenerator.generateRandomString(CaptchaGenerator.len)))) == null)
            ;
        Caches.getCache(CacheConfig.REGISTER_CODE).put(account, code);
        JSONObject json = new JSONObject();
        json.put("pic", Base64.encode(bs));
        json.put("width", CaptchaGenerator.len * CaptchaGenerator.defaultHeight / 2);
        json.put("height", CaptchaGenerator.defaultHeight);
        json.put("count", CaptchaGenerator.len);
        Caches.getCache(CacheConfig.REGISTER_CODE_IP).put(httpServletRequest.getRemoteHost(), true);
        return toResponse(json);
    }

    //    int USER_BASE_COUNT = 1000000;
    long USER_MIN_ID = 10000000;
    long USER_MAX_ID = USER_MIN_ID * 10;
    //    int userCount = 0;
//
//    int getUserCount() {
//        if (userCount == 0) {
//            userCount = SqlHelper.getService(IChatUserService.class).getBaseMapper().selectCount(new LambdaQueryWrapper<>());
//        }
//        return userCount;
//    }
//
//    Set<Long> uids = new HashSet<>();
//    int status = 0;
//
//    Set<Long> getUserIds() {
//        if (uids.size() == 0) {
//            uids = SqlHelper.getService(IChatUserService.class).list(new LambdaQueryWrapper<ChatUser>().select(ChatUser::getUserId).ge(ChatUser::getUserId, USER_MIN_ID).le(ChatUser::getUserId, USER_MAX_ID)).stream().map(ChatUser::getUserId).collect(Collectors.toSet());
//            status = 1;
//        }
//        if (uids.size() * 2 > USER_MAX_ID - USER_MIN_ID) {
//            List<Long> list = new ArrayList<>();
//            for (long i = USER_MIN_ID; i < USER_MAX_ID; i++) if (!uids.contains(i)) list.add(i);
//            uids = new HashSet<>(list);
//            status = 2;
//        }
//        return uids;
//    }
//
    IChatUserService service;

    IChatUserService getService() {
        if (service == null) {
            service = SqlHelper.getService(IChatUserService.class);
        }
        return service;
    }

    synchronized long getNewUserId() {
//        while (USER_MAX_ID - USER_MIN_ID <= getUserCount() - USER_BASE_COUNT) {
//            USER_MIN_ID = USER_MAX_ID;
//            USER_MAX_ID += USER_BASE_COUNT;
//        }
//        Set<Long> ids = getUserIds();
//        if (status == 2) {
//            List<Long> list = new ArrayList<>(ids);
//            long id = list.get(RandomUtil.randomInt(list.size()));
//            ids.remove(id);
//            userCount++;
//            return id;
//        } else {

        long id = RandomUtil.randomLong(USER_MAX_ID - USER_MIN_ID) + USER_MIN_ID;
        while (getService().getById(id) != null)
            id = RandomUtil.randomLong(USER_MAX_ID - USER_MIN_ID) + USER_MIN_ID;
//        ids.add(id);
//        userCount++;
        return id;
//        }
    }

    @CrossOrigin(origins = "*")
    @PostMapping(value = "/updatePwd")
    /** 修改用户密码 */
    public JSONObject updatePwd(@RequestBody JSONObject json) {
        Long userId = json.getLong("userId");
        Long session = json.getLong("session");
        String pwd = json.getString("pwd");
        if (userId == null || userId <= 0 || session == null || session <= 0 || StrUtil.isBlank(pwd))
            return toResponse(ErrorCode.PARAM_ERROR);
        Long auth = Caches.getCache(CacheConfig.USER_LOGIN_TOKEN).get(userId.toString(), Long.class);
        if (auth == null || !auth.equals(session)) {
            log.info("用户[" + userId + "]获取修改密码,身份验证[" + session + "==" + auth + "]失败!!!");
            return PluginController.toResponse(org.hcf.api.base.ErrorCode.TOKEN_ERROR);
        }
        ChatUser user = RedisUtils.getUser(userId);
        if (user == null) return PluginController.toResponse(org.hcf.api.base.ErrorCode.USER_NOT_EXIST);
        user.setPassword(MD5Util.encode(pwd));
        if (SqlHelper.getService(IChatUserService.class).updateById(user)) {
            RedisUtils.putUser(user);
            return toResponse(ErrorCode.SUCCESS);
        }
        return toResponse(ErrorCode.UPDATE_PWD_ERROR);
    }


    boolean saveAvatar(ChatUser user, String data) {
        if (StrUtil.isBlank(data)) return true;
        if (data.length() > 500) {
            byte[] bs = Base64.decode(data);
            if (bs != null) {
                String url = null;
                try {
                    InputStream in = ImageUtil.compressImage(bs, 0, 0);
                    url = save("avatar", user.getUserId().toString(), "jpg", in);
                } catch (Throwable ex) {
                    log.info("压缩图片异常!!!", ex);
                    try {
                        InputStream in = new ByteArrayInputStream(bs);
                        url = save("avatar", user.getUserId().toString(), "jpg", in);
                    } catch (Throwable ex1) {
                        log.error("保存文件异常", ex1);
                    }
                }
                if (StrUtil.isBlank(url)) return true;
                user.setAvatar(url);
                return true;
            }
        } else {
            user.setAvatar(data);
            return true;
        }
        return false;
    }

    public JSONObject toResponse(JSONObject data) {
        return toResponse(new HttpResponse(data));
    }

    public JSONObject toResponse(ErrorCode code) {
        return toResponse(new HttpResponse(code));
    }

    public JSONObject toResponse(HttpResponse obj) {
        return (JSONObject) JSONObject.toJSON(obj);
    }

    public String save(String type, String name, String suffix, InputStream in) {
        try {
            saveImage(getLocalName(type, name, suffix), in);
            return getRemoteUrl(type, name, suffix);
        } catch (Throwable ex) {
            log.error("保存文件[" + name + "." + suffix + "]异常", ex);
        }
        return "";
    }

    private String getRemoteDir(String type) {
        return Helper.RES.getUrl() + "/" + type;
    }

    String localDir = null;

    private String getLocalDir(String type) {
        if (localDir == null) {
            char[] arr = Helper.RES.getDir().trim().toCharArray();
            char lastChar = arr[arr.length - 1];
            localDir = (lastChar == '/' || lastChar == '\\' ? Helper.RES.getDir().trim() : Helper.RES.getDir().trim() + "/");
            File dir = new File(localDir + type);
            if (!dir.exists()) dir.mkdirs();
        }
        return localDir + type;
    }

    private String getRemoteUrl(String type, String name, String suffix) {
        return getRemoteDir(type) + "/" + name + "." + suffix;
    }

    private String getLocalName(String type, String name, String suffix) {
        return getLocalDir(type) + "/" + name + "." + suffix;
    }

    public void saveImage(String fileName, InputStream in) throws Exception {
        File file = new File(fileName);
        if (file.exists()) file.delete();
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(file);
            IOUtils.copy(in, out);
        } finally {
            if (out != null) {
                out.close();
            }
            if (in != null) {
                in.close();
            }
        }
    }
}
